/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import CoreMotion
import Flutter

public class AdaptiveVideoPlugin: NSObject, FlutterPlugin {
    private let motionManager = CMMotionManager()
    private let accelerometerStreamHandler = AccelerometerStreamHandler()
    private let screenOrientationStreamHandler = ScreenOrientationStreamHandler()
    private var lastOrientation = -1

    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(
            name: "hadss_adaptive_video", binaryMessenger: registrar.messenger())
        let instance = AdaptiveVideoPlugin(registrar: registrar)
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

    init(registrar: FlutterPluginRegistrar) {
        let GRAVITY_ORIENTATION_CHANNEL_NAME = "hadss_adaptive_video/gravity_orientation"
        let SCREEN_ORIENTATION_CHANNEL_NAME = "hadss_adaptive_video/screen_orientation"
        let accelerometerChannel = FlutterEventChannel(
            name: GRAVITY_ORIENTATION_CHANNEL_NAME, binaryMessenger: registrar.messenger())
        accelerometerChannel.setStreamHandler(accelerometerStreamHandler)

        let screenOrientationChannel = FlutterEventChannel(
            name: SCREEN_ORIENTATION_CHANNEL_NAME, binaryMessenger: registrar.messenger())
        screenOrientationChannel.setStreamHandler(screenOrientationStreamHandler)
    }

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        switch call.method {
        case "isSensorAvailable":
            let motionManager = CMMotionManager()
            result(motionManager.isAccelerometerActive)
        case "setGravityUpdateInterval":
            let arguments = call.arguments as! NSDictionary
            let timeInterval = TimeInterval(Double(arguments["interval"] as! Int) / 1000000.0)
            accelerometerStreamHandler.setUpdateInterval(timeInterval)
        default:
            if #available(iOS 16.0, *) {
                setOrientation(call, result)
            } else {
                setLegacyOrientation(call, result)
            }
        }
    }

    @available(iOS 16.0, *)
    private func setOrientation(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
            return
        }
        switch call.method {
        case "setPortraitUp":
            handleFixedOrientation(
                orientation: 0, mask: UIInterfaceOrientationMask.portrait, windowScene: windowScene)
        case "setPortraitUser":
            handleUseOrientation(
                mask: UIInterfaceOrientationMask.portrait, windowScene: windowScene)
        case "setLandscapeLeftUser":
            handleUseOrientation(
                mask: UIInterfaceOrientationMask.landscapeLeft, windowScene: windowScene)
        case "setLandscapeRightUser":
            handleUseOrientation(
                mask: UIInterfaceOrientationMask.landscapeRight, windowScene: windowScene)
        case "setAutoOrientationUser":
            handleFixedOrientation(
                orientation: -1, mask: UIInterfaceOrientationMask.allButUpsideDown,
                windowScene: windowScene)
        default:
            lastOrientation = -1
            accelerometerStreamHandler.removeOrientationChangeListener()
            result(FlutterMethodNotImplemented)
        }
    }

    @available(iOS 16.0, *)
    private func handleFixedOrientation(
        orientation: Int, mask: UIInterfaceOrientationMask, windowScene: UIWindowScene
    ) {
        lastOrientation = orientation
        accelerometerStreamHandler.removeOrientationChangeListener()
        windowScene.requestGeometryUpdate(
            .iOS(interfaceOrientations: mask))
    }

    @available(iOS 16.0, *)
    private func handleUseOrientation(mask: UIInterfaceOrientationMask, windowScene: UIWindowScene)
    {
        accelerometerStreamHandler.addOrientationChangeListener {
            orientation in
            if orientation != self.lastOrientation {
                self.lastOrientation = orientation
                windowScene.requestGeometryUpdate(
                    .iOS(interfaceOrientations: UIInterfaceOrientationMask.allButUpsideDown))
            }
        }
        windowScene.requestGeometryUpdate(
            .iOS(interfaceOrientations: mask))
    }

    private func setLegacyOrientation(_ call: FlutterMethodCall, _ result: @escaping FlutterResult)
    {
        switch call.method {
        case "setPortraitUp":
            handleLegacyFixedOrientation(orientation: 0, mask: UIInterfaceOrientation.portrait)
        case "setPortraitUser":
            handleLegacyUseOrientation(mask: UIInterfaceOrientation.portrait)
        case "setLandscapeLeftUser":
            handleLegacyUseOrientation(mask: UIInterfaceOrientation.landscapeLeft)
        case "setLandscapeRightUser":
            handleLegacyUseOrientation(mask: UIInterfaceOrientation.landscapeRight)
        case "setAutoOrientationUser":
            handleLegacyFixedOrientation(orientation: -1, mask: UIInterfaceOrientation.unknown)
        default:
            lastOrientation = -1
            accelerometerStreamHandler.removeOrientationChangeListener()
            result(FlutterMethodNotImplemented)
        }
    }

    private func handleLegacyFixedOrientation(orientation: Int, mask: UIInterfaceOrientation) {
        lastOrientation = orientation
        accelerometerStreamHandler.removeOrientationChangeListener()
        UIDevice.current.setValue(mask.rawValue, forKey: "orientation")
    }

    private func handleLegacyUseOrientation(mask: UIInterfaceOrientation) {
        accelerometerStreamHandler.addOrientationChangeListener {
            orientation in
            if orientation != self.lastOrientation {
                self.lastOrientation = orientation
                UIDevice.current.setValue(
                    UIInterfaceOrientation.unknown.rawValue, forKey: "orientation")
            }
        }
        UIDevice.current.setValue(mask.rawValue, forKey: "orientation")
    }
}
